Skip to content

ASCENDANT — 3D arena fighter with rollback netcode - #18

Draft
cloudygetty-ai wants to merge 3 commits into
mainfrom
claude/3d-action-multiplayer-game-cag2xb
Draft

cloudygetty-ai wants to merge 3 commits into
mainfrom
claude/3d-action-multiplayer-game-cag2xb

Conversation

@cloudygetty-ai

Copy link
Copy Markdown
Owner

A browser 3D arena fighter, added under ascendant/ as a self-contained subproject. It shares no code with the battle royale at the repo root.

This is a mirror. The project is intended to live in its own repository — it is here so the work is not stranded while cloudygetty-ai/ascendant does not yet exist.

What it is

Three lineages fused:

  • Street Fighter 6 — Drive gauge (Impact, Parry, Rush, Overdrive), real frame data with startup/active/recovery, cancel chains, counter hits, and Burnout when the gauge empties
  • Mortal Kombat — best-of-three rounds, Fatal Blow once per match below 30% health, and the finisher
  • Dragon Ball — flight, a ki economy whose only fast source is a defenceless charge stance, Vanish to escape a combo, contested beam clashes, and Ascension

The finisher

A match-ending blow does not kill. It leaves the loser standing and swaying while the announcer calls PUT HER UNDER. — the pronoun resolves from the loser's own character sheet, so HER, HIM or THEM. The winner then has 200 frames to land that character's named Erasure; let the window lapse and the match simply ends.

Architecture

ascendant/src/
├── engine/    pure simulation — no DOM, no three.js, no network
├── net/       rollback session, protocol, transports
├── render/    three.js scene, procedural rigs, poses, VFX, camera
├── ui/        HUD, menus, styles
└── client/    input mapping, fixed-timestep loop, bootstrap
ascendant/server/   WebSocket relay + lobby

Dependencies flow one way and a lint rule enforces it: the engine may not import from render, ui, net, client, or three.

Determinism

step(state, inputs, config) is pure — same state plus same inputs always produces the same next state, on every machine. That drove three constraints inside the engine:

  • Math.sin, Math.cos and Math.atan2 are banned. IEEE-754 exactly specifies + - * / and sqrt, but the transcendental functions are implementation-defined and drift between engines and CPUs. One ULP in a facing angle desyncs a match within seconds, so engine/math/trig.ts provides polynomial replacements.
  • Math.random is banned — a seeded xorshift128 lives in the state and is snapshot and restored with it.
  • No clocks.

checksum() hashes floats by exact bit pattern, so a one-ULP divergence is caught on the frame it happens.

Netcode

Local input applies immediately; the opponent's is predicted by repeating their last input, then corrected by rewind and resimulation when the real input contradicts it. The server is a relay that pairs players and forwards inputs — it never simulates, so it can never be the thing that desyncs a match.

Verification

Gate Result
tsc --noEmit (strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes) pass
eslint incl. the layering rule pass
vitest 91 passed
vite build pass
Headless browser run — menu, select, live combat pass

The decisive test: a peer running six frames of network delay lands on byte-identical state to a peer with none.

Notes for review

  • CI at the repo root does not cover ascendant/; its own workflow config ships in ascendant/.github/workflows/ci.yml and will only take effect once the project moves to its own repo.
  • The roster, stats and frame data are a coherent first pass, not a balanced game. Nothing has been playtested by a human — Helios's 130-damage solarFang and Korvath's armor are the kind of numbers only real matches expose.

🤖 Generated with Claude Code

https://claude.ai/code/session_017sxuEsAktMLBju5wmvGut8


Generated by Claude Code

claude added 3 commits August 24, 2026 10:38
ASCENDANT is a browser 3D arena fighter fusing Street Fighter 6's Drive
system and frame data, Mortal Kombat's rounds and finishers, and Dragon
Ball's flight, ki, beam clashes and transformations.

It lives under ascendant/ as a standalone package with its own build, tests
and CI config, and shares no code with the battle royale at the repo root.
This is a mirror: the project is intended to move to its own repository.

- Pure deterministic 60Hz simulation; step(state, inputs) is the contract
  rollback netcode depends on
- Deterministic trig and seeded PRNG replace Math.sin/cos/atan2 and
  Math.random, which are implementation-defined and desync replay
- GGPO-style rollback with prediction, resimulation and desync checksums,
  plus a WebSocket relay that never simulates a match
- Six fighters built procedurally from design tables, no art assets
- 91 tests covering determinism, rollback convergence, motion recognition,
  collision, damage, drive burnout and the finisher flow

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017sxuEsAktMLBju5wmvGut8
Adding ascendant/ broke two of this repo's own CI steps, because both sweep
the whole tree and the subproject ships a different toolchain:

- npm test: jest's testRegex matched all 9 of ascendant's *.test.ts files,
  which are written for Vitest. Every one failed with
  "Cannot find module 'vitest'" (reproduced locally: 9 suites failed).
- npm run lint: eslint . linted ascendant's sources against this repo's
  React Native + Prettier config, reporting formatting conflicts that carry
  no meaning across two projects with different style rules.

ascendant/ has its own ESLint config, tsconfig and test runner, so the fix
is to scope this repo's tooling to this repo. Type check was already safe —
the root tsconfig only includes src/.

Verified locally: jest 8 suites / 107 tests pass, tsc clean, and eslint
reports zero problems from ascendant/. The 87 problems eslint still reports
are pre-existing on main, in files this branch does not touch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017sxuEsAktMLBju5wmvGut8
The project is now ASCENDANTS, matching cloudygetty-ai/ascendants, so the
mirrored directory and the ignore patterns that scope this repo's tooling
follow it.

Verified after the rename: jest 8 suites / 107 tests pass, tsc clean, and
eslint reports zero problems from ascendants/. The 87 problems eslint still
reports are pre-existing on main, in files this branch does not touch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017sxuEsAktMLBju5wmvGut8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants